/*********************************************************************
*
*      Copyright (C) 2002 Andrew Khan
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
***************************************************************************/

package org.nokatag.jxl.write.biff;

import java.util.ArrayList;
import java.util.Iterator;

import org.nokatag.jxl.biff.IntegerHelper;
import org.nokatag.jxl.biff.StringHelper;
import org.nokatag.jxl.biff.Type;
import org.nokatag.jxl.biff.WritableRecordData;


/**
 * A continuation of a shared string table record.
 */
class SSTContinueRecord extends WritableRecordData
{
  /**
   * The first string is a special case
   */
  private String firstString;
  /**
   * Indicates whether or not we need to include the length information
   * for the first string
   */
  private boolean includeLength;
  /**
   * The length of the first string
   */
  private int firstStringLength;
  /**
   * The list of strings
   */
  private ArrayList strings;
  /**
   * The list of string lengths
   */
  private ArrayList stringLengths;
  /**
   * The binary data
   */
  private byte[] data;
  /**
   * The count of bytes needed so far to contain this record
   */
  private int byteCount;

  /**
   * The maximum amount of bytes available for the SST record
   */
  private static int maxBytes = 8228 - // max length
                                4;  // standard biff record stuff

  /**
   * Constructor
   *
   * @param numRefs the number of string references in the workbook
   * @param s the number of strings
   */
  public SSTContinueRecord()
  {
    super(Type.CONTINUE);

    byteCount = 0;
    strings = new ArrayList(50);
    stringLengths = new ArrayList(50);
  }

  /**
   * Adds the first string to this SST record
   *
   * @param s the string to add
   * @param b include the length information for the first string
   * @return the number of characters not added
   */
  public int setFirstString(String s, boolean b)
  {
    includeLength = b;
    firstStringLength = s.length();

    int bytes = 0;

    if (!includeLength)
    {
      bytes = s.length() * 2 + 1;
    }
    else
    {
      bytes = s.length() * 2 + 3;
    }

    if (bytes <= maxBytes)
    {
      firstString = s;
      byteCount += bytes;
      return 0;
    }

    // Calculate the number of characters we can add
    // The bytes variable will always be an odd number
    int charsAvailable = includeLength ? (maxBytes - 4) / 2 : 
                                         (maxBytes - 2) / 2;

    // Add what part of the string we can
    firstString = s.substring(0, charsAvailable);
    byteCount = maxBytes - 1;

    return s.length() - charsAvailable;
  }

  /**
   * Gets the current offset into this record, excluding the header fields
   *
   * @return the number of bytes after the header field
   */
  public int getOffset()
  {
    return byteCount;
  }

  /**
   * Adds a string to this  record.  It returns the number of string
   * characters not added, due to space constraints.  In the event
   * of this being non-zero, a continue record will be needed
   *
   * @param s the string to add
   * @return the number of characters not added
   */
  public int add(String s)
  {
    int bytes = s.length() * 2 + 3;

    // Must be able to add at least the first character of the string
    // onto the SST
    if (byteCount >= maxBytes - 5)
    {
      return s.length();
    }

    stringLengths.add(new Integer(s.length()));

    if (bytes + byteCount < maxBytes)
    {
      // add the string and return
      strings.add(s);
      byteCount += bytes;
      return 0;
    }

    // Calculate the number of characters we can add
    int bytesLeft = maxBytes - 3 - byteCount;
    int charsAvailable = bytesLeft % 2 == 0 ? bytesLeft / 2 :
                                             (bytesLeft - 1) / 2;

    // Add what part of the string we can
    strings.add(s.substring(0, charsAvailable));
    byteCount += charsAvailable * 2 + 3;

    return s.length() - charsAvailable;
  }

  /**
   * Gets the binary data for output to file
   * 
   * @return the binary data
   */
  public byte[] getData()
  {
    data = new byte[byteCount];

    int pos = 0;

    // Write out the first string
    if (includeLength)
    {
      IntegerHelper.getTwoBytes(firstStringLength, data, 0);
      data[2] = 0x01;
      pos = 3;
    }
    else
    {
      // Just include the unicode indicator
      data[0] = 0x01;
      pos = 1;
    }

    StringHelper.getUnicodeBytes(firstString, data, pos);
    pos += firstString.length() * 2;

    // Now write out the remainder of the strings
    Iterator i = strings.iterator();
    String s = null;
    int length = 0;
    int count = 0;
    while (i.hasNext())
    {
      s = (String) i.next();
      length = ( (Integer) stringLengths.get(count)).intValue();
      IntegerHelper.getTwoBytes(length, data, pos);
      data[pos+2] = 0x01;
      StringHelper.getUnicodeBytes(s, data, pos+3);
      pos += s.length() * 2 + 3;
      count++;
    }
    
    return data;
  }
}
